# -*- coding: utf-8 -*-
"""
Wargame AI的“上层AI” —— 用于比赛的第4阶段想定2030111194
2020-06
"""
__author__ = 'Yu XH'

import copy
from myAI.hexmap.handle_map import *
from myAI.attdamage.weapon_info import *
import time

class RedOprs:
    class TuJiDui_1:
        Tank, IFV, Infantry, UCV, CM_0, CM_1 = 0, 100, 200, 400, 700, 701
        # 初始位置：Tank: 1418, IFV: 1417, Infantry: 1417, UCV: 1417, CM_0: 1417, CM_1: 1417

    class TuJiDui_2:
        Tank, IFV, Infantry, UCV, CM_0, CM_1 = 1000, 1100, 1200, 1400, 1700, 1701
        # 初始位置：Tank: 1617, IFV: 1616, Infantry: 1616, UCV: 1616, CM_0: 1616, CM_1: 1616

    class TuJiDui_3:
        Tank, IFV, Infantry, UCV, CM_0, CM_1 = 2000, 2100, 2200, 2400, 2700, 2701
        # 初始位置：Tank: 1817, IFV: 1816, Infantry: 1816, UCV: 1816, CM_0: 1816, CM_1: 1816

    class TuJiDui_4:
        Tank, IFV, Infantry, UCV, CM_0, CM_1 = 3000, 3100, 3200, 3400, 3700, 3701
        # 初始位置：Tank: 1915, IFV: 1914, Infantry: 1914, UCV: 1914, CM_0: 1914, CM_1: 1914

    class ZhiShengJi:
        Helicopter_0, Helicopter_1 = 4600, 4601
        # 初始位置：Helicopter_0: 0619, Helicopter_1: 0719

    class WuRenJi:
        Drone_0, Drone_1 = 4500, 4501
        # 初始位置：Drone_0: 0919, Drone_1: 1019

    class PaoBingQun:
        Artillery_0, Artillery_1, Artillery_2, Artillery_3, Artillery_4, Artillery_5 = 5300, 5301, 5302, 5303, 5304, 5305
        # 初始位置：Artillery_0: 0806, Artillery_1: 0807, Artillery_2: 0808, Artillery_3: 0905, Artillery_4: 0906, Artillery_5: 0907


class BlueOprs:
    class XianQianDui_1:
        Tank, IFV_0, IFV_1, Infantry_0, Infantry_1 = 10000, 10100, 10101, 10200, 10201
        # 初始位置：Tank: 2739, IFV_0: 2639, IFV_1: 2640, Infantry_0: 2639, Infantry_1: 2640

    class XianQianDui_2:
        Tank, IFV_0, IFV_1, Infantry_0, Infantry_1 = 11000, 11100, 11101, 11200, 11201
        # 初始位置：Tank: 3139, IFV_0: 3039, IFV_1: 3040, Infantry_0: 3039, Infantry_1: 3040

    class XianQianDui_3:
        Tank, IFV_0, IFV_1, Infantry_0, Infantry_1 = 12000, 12100, 12101, 12200, 12201
        # 初始位置：Tank: 4127, IFV_0: 4027, IFV_1: 4028, Infantry_0: 4027, Infantry_1: 4028

    class XianQianDui_4:
        Tank, IFV_0, IFV_1, Infantry_0, Infantry_1 = 13000, 13100, 13101, 13200, 13201
        # 初始位置：Tank: 4230, IFV_0: 4129, IFV_1: 4130, Infantry_0: 4129, Infantry_1: 4130

    class ZhiShengJi:
        Helicopter_0, Helicopter_1 = 14600, 14601
        # 初始位置：Helicopter_0: 5557, Helicopter_1: 5558

    class WuRenJi:
        Drone_0, Drone_1 = 14500, 14501
        # 初始位置：Drone_0: 5657, Drone_1: 5658

    class PaoBingQun:
        Artillery_0, Artillery_1, Artillery_2, Artillery_3, Artillery_4, Artillery_5 = 15300, 15301, 15302, 15303, 15304, 15305
        # 初始位置：Artillery_0: 5455, Artillery_1: 5456, Artillery_2: 5457, Artillery_3: 5458, Artillery_4: 5555, Artillery_5: 5556


class FactionAI:
    def __init__(self, myAgent):
        self.color = myAgent.color
        self.scenario = myAgent.scenario
        self.max_step = 2880
        self.task = {}  # 公共变量，保存算子分配的任务信息{obj_id:['outputfire',param1,param2]}
        self.tactic = None  # 我方策略，'1-attack' | '2-defend'
        self.group_info = {}  # 保存算子分组信息 {group_name: {group_task: ['ToOccupy', city], obj_ids: [opr1, opr2]}}
        self.group_info_copy = {}  # 保存最初的算子分组，用于算子临时调离分组后的回归
        self.task_copy = {}  # factionAI运行中的操作对象，作用是减少对self.task的操作
        self.init_deploy_flag = True  # 开局部署兵力的标志
        self.last_observation = myAgent.scenario_info[-1]  # 保存上一步状态，便于和当前状态比较
        self.red_ai = FactionAIRed(myAgent)
        self.blue_ai = FactionAIBlue(myAgent)

    def reset(self):
        self.color = None
        self.scenario = None
        self.max_step = 2880
        self.task = {}
        self.tactic = None
        self.group_info = None
        self.task_copy = {}  # factionAI运行中的操作对象，作用是减少对self.task的操作
        self.last_observation = None  # 保存上一步状态，便于和当前状态比较

    def update(self, myAgent):
        try:
            while myAgent.thread_factionAI is not None:
                ############# 测试错误 ##########
                # time.sleep(0.5)
                # raise Exception('error')
                #################################

                if myAgent.observation['time']['cur_step'] <= self.last_observation['time']['cur_step']:
                    time.sleep(0.05)
                    continue

                if self.color == 0:
                    self.red_ai.update(self, myAgent)
                    # 写入任务
                    self.task = copy.deepcopy(self.red_ai.task)
                else:
                    self.blue_ai.update(self, myAgent)
                    # 写入任务
                    self.task = copy.deepcopy(self.blue_ai.task)
                # 更新上一次态势信息
                self.last_observation = myAgent.observation
            print(f'game over: thread_factionAI exit')
        # 如果factionAI执行出错，捕获异常，线程变量置空
        except Exception as e:
            print(f'thread_factionAI failure:')
            myAgent.thread_factionAI = None
        else:
            pass

class FactionAIRed:
    """红方AI"""

    def __init__(self, myAgent):
        self.color = myAgent.color
        self.scenario = myAgent.scenario
        self.max_step = 2880
        self.tactic = None  # 我方策略，'1-attack' | '2-defend'
        self.group_info = {}  # 保存动态分组信息 {group_name: {group_task: ['ToOccupy', city], obj_ids: [opr1, opr2]}}
        self.group_static = {}  # 保存静态分组信息，用于分组动态变化后的回归
        self.init_groups()  # 初始化算子分组，分组结果整场比赛不变化
        self.task = {}  # 与是factionAI的副本，保存算子分配的任务信息{obj_id:['outputfire',param1,param2]}
        self.task_backup = {}  # 用于临时备份某些任务，方便调度
        self.last_observation = myAgent.scenario_info[-1]  # 保存上一步状态，便于和当前状态比较
        self.di_mian_up = RedDiMianUp(self)
        self.di_mian_middle = RedDiMianMiddle(self)
        self.di_mian_down = RedDiMianDown(self)
        self.pao_bing = RedPaoBing(self)
        self.zhi_sheng_ji = RedZhiShengJi(self)
        self.wu_ren_ji = RedWuRenJi(self)
        self.xun_fei_dan = RedXunFeiDan(self)

    def update(self, factionAI, myAgent):
        self.task = copy.deepcopy(factionAI.task)

        our_oprs = myAgent.get_our_oprs()

        # 没有可以使用的算子
        if not our_oprs:
            self.task = {}
            return

        # 如果我方有算子死亡，则将其任务删除
        our_oprs_id = [opr['obj_id'] for opr in our_oprs]
        need_pop = [opr_id for opr_id in self.task.keys() if opr_id not in our_oprs_id]
        for pop_id in need_pop:
            self.task.pop(pop_id)

        # 如果我方算子有无效任务（Success, Failure, Error等），将任务删除
        for opr in our_oprs:
            opr_id = opr['obj_id']
            if opr_id in self.task.keys():
                if self.task[opr_id][0] in ['Success', 'Failure', 'Error']:
                    self.task.pop(opr_id)

        # 将死亡算子的信息从group_info中移除
        self.remove_group_dead_members(myAgent)

        # 对我方算子进行重分组 -- 分组策略
        self.regroup_oprs(myAgent)

        # 分组任务分派
        self.di_mian_up.plan(self, myAgent)
        self.di_mian_middle.plan(self, myAgent)
        self.di_mian_down.plan(self, myAgent)
        self.pao_bing.plan(self, myAgent)
        self.zhi_sheng_ji.plan(self, myAgent)
        self.wu_ren_ji.plan(self, myAgent)
        self.xun_fei_dan.plan(self, myAgent)

        # 跨组任务分派
        self.plan_output_firepower(myAgent)
        self.plan_control(myAgent)

        # 应急响应
        self.hide_helicopter(myAgent)

    def init_groups(self):
        self.group_static['pao_bing'] = [RedOprs.PaoBingQun.Artillery_0, RedOprs.PaoBingQun.Artillery_1,
                                         RedOprs.PaoBingQun.Artillery_2, RedOprs.PaoBingQun.Artillery_3,
                                         RedOprs.PaoBingQun.Artillery_4, RedOprs.PaoBingQun.Artillery_5]
        self.group_info['pao_bing'] = {'group_task': [], 'obj_ids': self.group_static['pao_bing']}
        self.group_static['zhi_sheng_ji'] = [RedOprs.ZhiShengJi.Helicopter_0, RedOprs.ZhiShengJi.Helicopter_1]
        self.group_info['zhi_sheng_ji'] = {'group_task': [], 'obj_ids': self.group_static['zhi_sheng_ji']}
        self.group_static['wu_ren_ji'] = [RedOprs.WuRenJi.Drone_0, RedOprs.WuRenJi.Drone_1]
        self.group_info['wu_ren_ji'] = {'group_task': [], 'obj_ids': self.group_static['wu_ren_ji']}
        self.group_static['xun_fei_dan'] = [RedOprs.TuJiDui_1.CM_0, RedOprs.TuJiDui_1.CM_1,
                                            RedOprs.TuJiDui_2.CM_0, RedOprs.TuJiDui_2.CM_1,
                                            RedOprs.TuJiDui_3.CM_0, RedOprs.TuJiDui_3.CM_1,
                                            RedOprs.TuJiDui_4.CM_0, RedOprs.TuJiDui_4.CM_1]
        self.group_info['xun_fei_dan'] = {'group_task': [], 'obj_ids': []}
        self.group_static['di_mian_up'] = [RedOprs.TuJiDui_1.Tank, RedOprs.TuJiDui_2.Tank,
                                           RedOprs.TuJiDui_3.Tank]  # 上路地面组
        self.group_info['di_mian_up'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_up']}
        self.group_static['di_mian_middle'] = [RedOprs.TuJiDui_1.IFV, RedOprs.TuJiDui_1.UCV, RedOprs.TuJiDui_1.Infantry,
                                               RedOprs.TuJiDui_2.IFV, RedOprs.TuJiDui_2.UCV, RedOprs.TuJiDui_2.Infantry,
                                               RedOprs.TuJiDui_3.IFV, RedOprs.TuJiDui_3.UCV, RedOprs.TuJiDui_3.Infantry]
        self.group_info['di_mian_middle'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_middle']}
        self.group_static['di_mian_down'] = [RedOprs.TuJiDui_4.Tank, RedOprs.TuJiDui_4.IFV,
                                             RedOprs.TuJiDui_4.Infantry, RedOprs.TuJiDui_4.UCV]  # 下路地面组
        self.group_info['di_mian_down'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_down']}

    def remove_group_dead_members(self, myAgent):
        """删除group_info中的已死亡算子，group_static不变化"""
        for obj_id in self.group_info['pao_bing']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['pao_bing']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['zhi_sheng_ji']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['zhi_sheng_ji']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['wu_ren_ji']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['wu_ren_ji']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['xun_fei_dan']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['xun_fei_dan']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_up']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_up']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_middle']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_middle']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_down']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_down']['obj_ids'].remove(obj_id)

    def regroup_oprs(self, myAgent):
        """重新分组"""
        """=== 空闲算子按照group_static进行分组 ==="""
        our_oprs = myAgent.get_our_oprs()
        for opr in our_oprs:
            opr_id = opr['obj_id']
            regroup_flag = False
            if opr_id in self.task.keys():
                if self.task[opr_id][0] in ['Success', 'Failure', 'Error'] and opr['on_board'] == 0:
                    regroup_flag = True
            else:
                if opr['on_board'] == 0:
                    regroup_flag = True
            if regroup_flag:
                group_name = None
                for name, obj_ids in self.group_static.items():
                    if opr_id in obj_ids:
                        group_name = name
                if group_name:
                    if opr_id not in self.group_info[group_name]['obj_ids']:
                        self.group_info[group_name]['obj_ids'].append(opr_id)
                if self.task_backup.get(opr_id):  # 如果空闲算子在self.task_backup中有备份，继续备份中的任务
                    self.task[opr_id] = self.task_backup.pop(opr_id)

        """=== 调整分组 ==="""
        # 暂无需要调整分组的情形

    def plan_control(self, myAgent):
        if myAgent.observation['time']['cur_step'] <= 1500:
            return False
        our_tanks, tar_cities = [], []
        for opr in myAgent.get_our_oprs():
            if opr['sub_type'] == BopType.Tank:
                if opr['obj_id'] in self.task.keys():
                    if 'control' not in self.task[opr['obj_id']]:
                        our_tanks.append(opr)
                    else:
                        tar_cities.append(self.task[opr['obj_id']][1])
                else:
                    our_tanks.append(opr)
        if not our_tanks:
            return
        other_cities = []
        for city in myAgent.observation['cities']:
            if city['flag'] != self.color:
                if city['coord'] in tar_cities:
                    continue
                flag_enemy_around = False
                for enemy_info in myAgent.situations.enemies:
                    if not enemy_info['spread_flag']:
                        continue
                    enemy = enemy_info['operator']
                    if enemy['type'] == BopType.Aircraft:
                        continue
                    if cal_distance(enemy['cur_hex'], city['coord']) <= 1:
                        flag_enemy_around = True
                        break
                if not flag_enemy_around:
                    other_cities.append(city)
        if not other_cities:
            return
        no_vehicles_enemy = True
        for enemy_info in myAgent.situations.enemies:
            if not enemy_info['spread_flag']:
                continue
            enemy = enemy_info['operator']
            if enemy['type'] == BopType.Vehicle and enemy['sub_type'] != BopType.Artillery:
                no_vehicles_enemy = False
                break
        if not no_vehicles_enemy:
            return
        min_dist = 1000
        best_opr, best_city = None, None
        for tank in our_tanks:
            if 'control' not in self.task[tank['obj_id']]:
                for city in other_cities:
                    cur_dist = cal_distance(city['coord'], tank['cur_hex'])
                    if min_dist > cur_dist:
                        best_opr = tank
                        best_city = city
                        min_dist = cur_dist
        self.task[best_opr['obj_id']] = ['control', best_city['coord']]

    def plan_output_firepower(self, myAgent):
        if not self.check_enter_firepower_phase(myAgent):
            return
        # # 确定进攻区域
        # fire_area = []
        # for enemy_info in myAgent.situations.enemies:
        #     if not enemy_info['spread_flag']:
        #         continue
        #     enemy = enemy_info['operator']
        #     if enemy['type'] == BopType.Aircraft or enemy['sub_type'] == BopType.Artillery:
        #         continue
        #     if enemy['type'] == BopType.Infantry:
        #         fire_area.append(enemy['cur_hex'])
        #     else:
        #         fire_area += get_range(enemy['cur_hex'], 1, myAgent.get_map_size())
        # fire_area = list(set(fire_area))
        # 为所有车辆算子分派火力推进任务
        for opr in myAgent.get_our_oprs():
            if opr['type'] != BopType.Vehicle:
                continue
            if opr['sub_type'] == BopType.Artillery:
                continue
            self.task[opr['obj_id']] = ['fire_propulsion']

    def hide_helicopter(self, myAgent):
        # 遭到直升机突袭的反应策略（不考虑车辆的反应）
        map_size = myAgent.get_map_size()
        n_hexes = map_size['row'] * map_size['col']
        dist_matrix = myAgent.my_map['dist_matrix']
        enemy_heli_0 = myAgent.get_bop(BlueOprs.ZhiShengJi.Helicopter_0)
        enemy_heli_1 = myAgent.get_bop(BlueOprs.ZhiShengJi.Helicopter_1)
        if not enemy_heli_0 and not enemy_heli_1:
            return
        enemy_helis = []
        if enemy_heli_0:
            enemy_helis.append(enemy_heli_0)
        if enemy_heli_1:
            enemy_helis.append(enemy_heli_1)
        # 计算直升机的威胁范围
        threat_area_vehicle_bool = np.zeros(n_hexes, dtype=bool)
        shoot_area_infantry_bool = np.zeros(n_hexes, dtype=bool)
        for heli in enemy_helis:
            # heli_info = myAgent.get_enemy_info(heli['obj_id'])
            heli_loc_ind = mapid_2_matind(heli['cur_hex'], map_size)
            # threat_area_vehicle_bool = np.logical_or(threat_area_vehicle_bool,
            #                                          get_opr_shoot_range(heli, BopType.Vehicle, myAgent,
            #                                                              heli['carry_weapon_ids'],
            #                                                              heli_info['remain_bullet_nums']))
            shoot_area_infantry_bool = np.logical_or(shoot_area_infantry_bool, dist_matrix[heli_loc_ind] <= 10)
        # threat_area_vehicle = list(matind_2_mapid(np.arange(n_hexes)[threat_area_vehicle_bool], map_size))
        shoot_area_infantry = list(matind_2_mapid(np.arange(n_hexes)[shoot_area_infantry_bool], map_size))
        for opr in myAgent.get_our_oprs():
            if opr['type'] == BopType.Infantry:
                """步兵的反应：停下来准备射击"""
                if opr['remain_bullet_nums'][BulletType.HeavyMissile] <= 0:
                    continue
                if opr['cur_hex'] not in shoot_area_infantry:
                    continue
                ambush_loc = opr['move_path'][-1] if opr['move_path'] else opr['cur_hex']
                if self.task.get(opr['obj_id']):
                    if 'air_defense' not in self.task[opr['obj_id']] and not self.task_backup.get(opr['obj_id']):  # 本策略中'air_defense'的任务只用于步兵打直升机
                        self.task_backup[opr['obj_id']] = self.task.pop(opr['obj_id'])
                    self.task[opr['obj_id']] = ['air_defense', ambush_loc]
            # elif opr['type'] == BopType.Vehicle:
            #     """车辆的反应：能逃就逃"""
            #     if opr['cur_hex'] not in threat_area_vehicle:
            #         continue
            #     if self.task.get(opr['obj_id']):
            #         if 'hide' not in self.task[opr['obj_id']] and not self.task_backup.get(opr['obj_id']):
            #             self.task_backup[opr['obj_id']] = self.task.pop(opr['obj_id'])
            #         self.task[opr['obj_id']] = ['hide']

    def check_enter_firepower_phase(self, myAgent):
        """判断红方地面组是否执行夺控任务"""
        if myAgent.observation['time']['cur_step'] >= 1500:  # 留1000s用于夺控
            return True
        no_vehicles_enemy = True
        for enemy_info in myAgent.situations.enemies:
            enemy = enemy_info['operator']
            if enemy['type'] == BopType.Vehicle and enemy['sub_type'] != BopType.Artillery:
                no_vehicles_enemy = False
                break
        if no_vehicles_enemy:  # 敌方没有车辆，实施夺控
            return True
        missile_count = 0
        for opr in myAgent.get_our_oprs():
            if opr['sub_type'] == BopType.IFV:
                missile_count += opr['remain_bullet_nums'][BulletType.HeavyMissile]
        if missile_count <= 0:  # 我方战车导弹耗尽，实施夺控
            return True
        return False


class RedDiMianUp:
    """上路地面组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_up']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_up']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        pass

    def assign_opr_tasks(self, factionAI, myAgent):
        tank_1 = myAgent.get_bop(RedOprs.TuJiDui_1.Tank)
        tank_2 = myAgent.get_bop(RedOprs.TuJiDui_2.Tank)
        tank_3 = myAgent.get_bop(RedOprs.TuJiDui_3.Tank)

        if 'Deployment' in self.group_task:
            """=== 部署任务 ==="""
            if tank_1:
                if tank_1['cur_hex'] == 1418:
                    factionAI.task[tank_1['obj_id']] = ['maneuver', 1328]
                if tank_1['cur_hex'] == 1328:
                    factionAI.task[tank_1['obj_id']] = ['ambush4th', 1733, False]
            if tank_2:
                if tank_2['cur_hex'] == 1617:
                    factionAI.task[tank_2['obj_id']] = ['ambush4th', 2330, False]
            if tank_3:
                if tank_3['cur_hex'] == 1817:
                    factionAI.task[tank_3['obj_id']] = ['ambush4th', 2431, False]


class RedDiMianMiddle:
    """上路地面组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_middle']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_middle']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        pass

    def assign_opr_tasks(self, factionAI, myAgent):
        ifv_1, ucv_1 = myAgent.get_bop(RedOprs.TuJiDui_1.IFV), myAgent.get_bop(RedOprs.TuJiDui_1.UCV)
        infantry_1, ifv_2 = myAgent.get_bop(RedOprs.TuJiDui_1.Infantry), myAgent.get_bop(RedOprs.TuJiDui_2.IFV)
        ucv_2, infantry_2 = myAgent.get_bop(RedOprs.TuJiDui_2.UCV), myAgent.get_bop(RedOprs.TuJiDui_2.Infantry)
        ifv_3, ucv_3 = myAgent.get_bop(RedOprs.TuJiDui_3.IFV), myAgent.get_bop(RedOprs.TuJiDui_3.UCV)
        infantry_3 = myAgent.get_bop(RedOprs.TuJiDui_3.Infantry)

        if 'Deployment' in self.group_task:
            """=== 部署任务 ==="""
            if ifv_1:
                cm_0_1 = myAgent.get_bop(RedOprs.TuJiDui_1.CM_0)
                if cm_0_1 and infantry_1:
                    if ifv_1['cur_hex'] == 1417 and cm_0_1['on_board'] == 1 and cm_0_1['get_off_remain_time'] <= 0:
                        factionAI.task[ifv_1['obj_id']] = ['unload', RedOprs.TuJiDui_1.CM_0, False]
                    if ifv_1['cur_hex'] == 1417 and cm_0_1['get_off_remain_time'] > 0:
                        factionAI.task[ifv_1['obj_id']] = ['deliver', infantry_1['obj_id'], 2427]
                if infantry_1:
                    if ifv_1['cur_hex'] == 2427 and infantry_1['get_off_remain_time'] > 0:
                        factionAI.task[ifv_1['obj_id']] = ['ambush4th', 2427, False]
                        factionAI.task[infantry_1['obj_id']] = ['ambush4th', 2530, True]
            if ifv_2:
                cm_0_2 = myAgent.get_bop(RedOprs.TuJiDui_2.CM_0)
                if cm_0_2 and infantry_2:
                    if ifv_2['cur_hex'] == 1616 and cm_0_2['on_board'] == 1 and cm_0_2['get_off_remain_time'] <= 0:
                        factionAI.task[ifv_2['obj_id']] = ['unload', RedOprs.TuJiDui_2.CM_0, False]
                    if ifv_2['cur_hex'] == 1616 and cm_0_2['get_off_remain_time'] > 0:
                        factionAI.task[ifv_2['obj_id']] = ['deliver', infantry_2['obj_id'], 2526]
                if infantry_2:
                    if ifv_2['cur_hex'] == 2526 and infantry_2['get_off_remain_time'] > 0:
                        factionAI.task[ifv_2['obj_id']] = ['ambush4th', 2426, False]
                        factionAI.task[infantry_2['obj_id']] = ['ambush4th', 2629, True]
            if ifv_3:
                cm_0_3 = myAgent.get_bop(RedOprs.TuJiDui_3.CM_0)
                if cm_0_3 and infantry_3:
                    if ifv_3['cur_hex'] == 1816 and cm_0_3['on_board'] == 1 and cm_0_3['get_off_remain_time'] <= 0:
                        factionAI.task[ifv_3['obj_id']] = ['unload', RedOprs.TuJiDui_3.CM_0, False]
                    if ifv_3['cur_hex'] == 1816 and cm_0_3['get_off_remain_time'] > 0:
                        factionAI.task[ifv_3['obj_id']] = ['deliver', infantry_3['obj_id'], 1920]
                if infantry_3:
                    if ifv_3['cur_hex'] == 1920 and infantry_3['get_off_remain_time'] > 0:
                        factionAI.task[ifv_3['obj_id']] = ['ambush4th', 2423, False]
                        factionAI.task[infantry_3['obj_id']] = ['ambush4th', 1923, True]
            if ucv_1:
                if ucv_1['cur_hex'] == 1417:
                    factionAI.task[ucv_1['obj_id']] = ['ambush4th', 2326, True]
            if ucv_2:
                if ucv_2['cur_hex'] == 1616:
                    factionAI.task[ucv_2['obj_id']] = ['ambush4th', 2423, True]
            if ucv_3:
                if ucv_3['cur_hex'] == 1816:
                    factionAI.task[ucv_3['obj_id']] = ['ambush4th', 2822, True]


class RedDiMianDown:
    """下路地面组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_down']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_down']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        pass

    def assign_opr_tasks(self, factionAI, myAgent):
        tank_4, ifv_4 = myAgent.get_bop(RedOprs.TuJiDui_4.Tank), myAgent.get_bop(RedOprs.TuJiDui_4.IFV)
        infantry_4, ucv_4 = myAgent.get_bop(RedOprs.TuJiDui_4.Infantry), myAgent.get_bop(RedOprs.TuJiDui_4.UCV)

        if 'Deployment' in self.group_task:
            """=== 部署任务 ==="""
            if ifv_4:
                cm_0_4 = myAgent.get_bop(RedOprs.TuJiDui_4.CM_0)
                if cm_0_4 and infantry_4:
                    if ifv_4['cur_hex'] == 1914 and cm_0_4['on_board'] == 1 and cm_0_4['get_off_remain_time'] <= 0:
                        factionAI.task[ifv_4['obj_id']] = ['unload', RedOprs.TuJiDui_4.CM_0, False]
                    if ifv_4['cur_hex'] == 1914 and cm_0_4['get_off_remain_time'] > 0:
                        factionAI.task[ifv_4['obj_id']] = ['deliver', infantry_4['obj_id'], 2416]
                    if ifv_4['cur_hex'] == 2416 and infantry_4['get_off_remain_time'] > 0:
                        factionAI.task[ifv_4['obj_id']] = ['ambush4th', 2827, False]
                        factionAI.task[infantry_4['obj_id']] = ['ambush4th', 2914, True]
            if tank_4:
                if tank_4['cur_hex'] == 1915:
                    factionAI.task[tank_4['obj_id']] = ['ambush4th', 3221, False]
            if ucv_4:
                if ucv_4['cur_hex'] == 1914:
                    factionAI.task[ucv_4['obj_id']] = ['ambush4th', 3319, True]


class RedPaoBing:
    """炮兵组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['pao_bing']
        self.group_task = []
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['pao_bing']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        """炮兵组任务"""
        """=== 清空炮兵组任务 ==="""
        flag_task_empty = True
        for arti_id in self.members:
            arti = myAgent.get_bop(arti_id)
            if arti:
                if arti_id in factionAI.task.keys():
                    flag_task_empty = False
                    break
        if flag_task_empty:
            self.group_task = []

        """=== 删除死亡的校射算子 ==="""
        if self.group_task:
            if isinstance(self.group_task[2], int):
                directing_opr_id = self.group_task[2]
                if not myAgent.get_bop(directing_opr_id):
                    self.group_task[2] = None

        """=== 关键点位压制任务 ==="""
        if self.new_plan_check(myAgent):
            if self.cur_step <= 150:
                self.group_task = ['FireSupport', 2737, None]  # 第1波压制
                return

        """=== 常规任务 ==="""
        if self.cur_step <= 150:
            return
        if not self.new_plan_check(myAgent):
            return
        # --- 计算飞行时间 ---
        fly_time = 0
        for jm_info in myAgent.observation['jm_points']:
            if jm_info['obj_id'] in self.members:
                if jm_info['fly_time'] > fly_time:
                    fly_time = jm_info['fly_time']
        if fly_time > 100:  # 快要达到的火力不打断
            return
        # --- 确定炮兵目标 ---
        being_jm_hexes = []  # 正在被间瞄损伤的六角格
        for jm_info in myAgent.observation['jm_points']:
            if jm_info['status'] == 0:  # 正在飞行
                being_jm_hexes.append(jm_info['pos'])
            elif jm_info['status'] == 1 and jm_info['boom_time'] < 150:
                being_jm_hexes.append(jm_info['pos'])
        being_jm_hexes = list(set(being_jm_hexes))
        being_planed_hexes = []  # 在计划中的间瞄目标
        if self.group_task:
            being_planed_hexes.append(self.group_task[1])
        near_by_hexes = []  # 我方算子周边格
        for opr in myAgent.get_our_oprs():
            if opr['type'] != BopType.Aircraft:
                opr_loc = opr['cur_hex']
                near_by_hexes += get_range(opr_loc, 1, myAgent.get_map_size())
        near_by_hexes = list(set(near_by_hexes))
        targets = get_artillery_target(myAgent)
        for key, value in targets.items():  # 过滤正在遭受打击的目标，以及列入间瞄任务的目标
            if not value:
                for tar_id in value:
                    tar_loc = myAgent.get_bop(tar_id)['cur_hex']
                    if tar_loc in being_jm_hexes + being_planed_hexes + near_by_hexes:
                        targets[key].remove(tar_id)
        # --- 有受压制的步兵时，优先打击此类目标 ---
        if targets[0]:
            target = targets[0][0]
            if self.group_task:
                cur_tar_loc = self.group_task[1]
                if check_keep_infantry_in_hex(cur_tar_loc, myAgent.situations.enemies):  # 当前的打击目标就是压制步兵
                    return
            tar_loc = myAgent.get_enemy(target)['cur_hex']
            self.group_task = ['FireSupport', tar_loc, None]
        elif not self.group_task:
            # === 没有受压制的步兵时，根据空闲的炮兵组，按照优先顺序分派jm任务 ===
            # 筛选炮击目标六角格
            candidate_targets = None
            for m in range(1, 4):
                if targets[m]:
                    candidate_targets = targets[m]
                    break
            if candidate_targets:
                arti_locs = []
                for arti_id in self.members:
                    arti = myAgent.get_bop(arti_id)
                    if arti:
                        arti_locs.append(arti['cur_hex'])
                candidate_locs = [myAgent.get_enemy_info(obj_id)['operator']['cur_hex'] for obj_id in
                                  candidate_targets]
                candidate_locs = list(set(candidate_locs))
                tar_loc = get_min_dist_loc(candidate_locs, arti_locs, myAgent)
                self.group_task = ['FireSupport', tar_loc, None]

        """=== 为炮兵组任务分配校射算子 ==="""
        if self.cur_step > 450:
            if not self.group_task:
                return
            if isinstance(self.group_task[2], int):
                return
            fly_time = 0
            for jm_info in myAgent.observation['jm_points']:
                if jm_info['obj_id'] in self.members:
                    if jm_info['fly_time'] > fly_time:
                        fly_time = jm_info['fly_time']
            if fly_time > 50:  # 只为早期任务分配校射算子
                return
            # 筛选校射算子
            tar_loc = self.group_task[1]
            directing_opr_id = self.get_directing_opr(tar_loc, myAgent)
            if directing_opr_id:
                self.group_task[2] = directing_opr_id  # 完善炮兵组任务
                group_name = get_group_by_obj_id(directing_opr_id, factionAI)
                if group_name:
                    factionAI.group_info[group_name]['obj_ids'].remove(directing_opr_id)  # 暂时将校射算子调离原分组
                    if factionAI.task.get(directing_opr_id) and not factionAI.task_backup.get(directing_opr_id):
                        factionAI.task_backup[directing_opr_id] = factionAI.task.pop(directing_opr_id)

    def assign_opr_tasks(self, factionAI, myAgent):
        """为算子分派任务"""
        """=== 清除完成的校射任务 ==="""
        if self.group_task:
            if isinstance(self.group_task[2], int):
                directing_opr_ids = [self.group_task[2]]
            else:
                directing_opr_ids = []
            for opr in myAgent.get_our_oprs():
                obj_id = opr['obj_id']
                if obj_id not in factionAI.task.keys():
                    continue
                if 'direct_artilleries' in factionAI.task[obj_id]:
                    if not directing_opr_ids:
                        factionAI.task.pop(obj_id)
                    else:
                        if obj_id not in directing_opr_ids:
                            factionAI.task.pop(obj_id)

        """=== 分派炮兵齐射任务 ==="""
        if self.group_task:
            for arti_id in self.members:
                factionAI.task[arti_id] = ['jm_salvo', self.group_task[1]]

        """=== 分派校射任务 ==="""
        if self.group_task:
            if isinstance(self.group_task[2], int):
                directing_opr_id = self.group_task[2]
                if directing_opr_id in factionAI.task.keys():
                    if factionAI.task[directing_opr_id] == ['direct_artilleries', self.group_task[1]]:
                        return
                factionAI.task[directing_opr_id] = ['direct_artilleries', self.group_task[1]]

    def get_directing_opr(self, tar_loc, myAgent):
        """为炮兵组选择校射算子"""
        # === 确定对tar_enemies的可视半径 ===
        observe_radius = {}  # 最小可观察范围
        tar_loc_terrain = get_terrain_info(myAgent.map.get_map_data(), tar_loc)
        if tar_loc_terrain in [TerrainType.Forest, TerrainType.Resident]:
            terrain_flag = True
        else:
            terrain_flag = False
        if terrain_flag:
            observe_radius[BopType.Infantry] = 12
            observe_radius[BopType.Vehicle] = 12
            observe_radius[BopType.Drone] = 1
        else:
            observe_radius[BopType.Infantry] = 25
            observe_radius[BopType.Vehicle] = 25
            observe_radius[BopType.Drone] = 2
        # === 计算对tar_enemies的最小观察区域 ===
        dist_matrix = myAgent.my_map['dist_matrix']
        map_size = myAgent.get_map_size()
        n_hexes = map_size['row'] * map_size['col']
        tar_loc_ind = mapid_2_matind(tar_loc, myAgent.get_map_size())
        dist_vec = dist_matrix[tar_loc_ind]
        see_vec_ground = myAgent.my_map['see_matrix'][0][tar_loc_ind]
        see_vec_air = myAgent.my_map['see_matrix'][2][tar_loc_ind]
        observe_area = {}
        for bop_type, radius in observe_radius.items():
            if bop_type in [BopType.Infantry, BopType.Vehicle]:
                observe_area[bop_type] = np.arange(n_hexes)[dist_vec <= observe_radius[bop_type] & see_vec_ground]
            else:
                observe_area[bop_type] = np.arange(n_hexes)[dist_vec <= observe_radius[bop_type] & see_vec_air]
        # === 筛选出能够在150s内抵达最小观察区域的我方算子 ===
        cost_matrix_vehicle = myAgent.my_map['cost_matrix'][0]
        cost_matrix_infantry = myAgent.my_map['cost_matrix'][1]
        cost_matrix_little_aircraft = dist_matrix * 7.2
        candidate_oprs = []
        for opr in myAgent.get_our_oprs():
            if opr['sub_type'] not in [BopType.Artillery, BopType.CruiseMissile, BopType.Helicopter]:
                opr_loc_ind = mapid_2_matind(opr['cur_hex'], map_size)
                if opr['type'] == BopType.Infantry:
                    cost_vec = cost_matrix_infantry[opr_loc_ind]
                    if np.any(cost_vec[observe_area[BopType.Infantry]] < 150):
                        candidate_oprs.append(opr)
                elif opr['type'] == BopType.Vehicle:
                    cost_vec = cost_matrix_vehicle[opr_loc_ind]
                    if np.any(cost_vec[observe_area[BopType.Vehicle]] < 150):
                        candidate_oprs.append(opr)
                elif opr['sub_type'] == BopType.Drone:
                    cost_vec = cost_matrix_little_aircraft[opr_loc_ind]
                    if np.any(cost_vec[observe_area[BopType.Drone]] < 150):
                        candidate_oprs.append(opr)
        # 进一步从candidate_oprs筛选出最优的校射算子
        if not candidate_oprs:
            return None
        tar_enemies = []
        for enemy_info in myAgent.situations.enemies:
            if enemy_info['spread_flag']:
                enemy = enemy_info['operator']
                ene_loc = enemy['cur_hex']
                if enemy['type'] != BopType.Aircraft:
                    if enemy['armor'] != ArmorType.Composit:
                        if ene_loc == tar_loc:
                            if np.max(enemy_info['pos_probs']) > 1 - 1e-8:
                                tar_enemies.append(enemy)
        if not tar_enemies:
            return None
        infantry_flag = False  # 确定是否有步兵算子
        for enemy in tar_enemies:
            if enemy['type'] == BopType.Infantry:
                infantry_flag = True
                break
        drone_oprs, vehicle_oprs, infantry_oprs = [], [], []
        for opr in candidate_oprs:
            if opr['sub_type'] == BopType.Drone:
                drone_oprs.append(opr)
            elif opr['type'] == BopType.Vehicle:
                vehicle_oprs.append(opr)
            else:
                infantry_oprs.append(opr)
        poss_oprs = None
        if infantry_flag:  # 如果有步兵
            keep_flag = True  # 步兵算子是否处于压制状态
            for enemy in tar_enemies:
                if enemy['type'] == BopType.Infantry and enemy['keep'] == 0:
                    keep_flag = False
                    break
            if keep_flag:  # 步兵算子处于压制状态时的校射算子筛选
                if infantry_oprs:
                    poss_oprs = infantry_oprs
                elif drone_oprs:
                    poss_oprs = drone_oprs
                elif vehicle_oprs:
                    poss_oprs = vehicle_oprs
            else:  # 步兵算子没有处于压制状态的情况
                if infantry_oprs:
                    poss_oprs = infantry_oprs
                elif vehicle_oprs:
                    poss_oprs = vehicle_oprs
        else:  # 没有步兵算子
            if drone_oprs:
                poss_oprs = drone_oprs
            elif infantry_oprs:
                poss_oprs = infantry_oprs
            elif vehicle_oprs:
                poss_oprs = vehicle_oprs
        best_opr_id = None
        if poss_oprs:
            min_dist = 1000
            for opr in poss_oprs:
                dist = myAgent.map.get_distance(opr['cur_hex'], tar_loc)
                if min_dist > dist:
                    min_dist = dist
                    best_opr_id = opr['obj_id']
        return best_opr_id

    def new_plan_check(self, myAgent):
        for anti_id in self.members:
            if myAgent.observation['valid_actions'].get(anti_id):
                if not myAgent.observation['valid_actions'][anti_id].get(ActionType.JMPlan):
                    return False
        return True


class RedZhiShengJi:
    """直升机组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['zhi_sheng_ji']
        self.group_task = []
        self.tactic = ['Deployment']
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['zhi_sheng_ji']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 火力输出任务 ---
        if self.cur_step >= 300:
            self.group_task = ['OutputFire']

    def assign_opr_tasks(self, factionAI, myAgent):
        heli_0 = myAgent.get_bop(RedOprs.ZhiShengJi.Helicopter_0)
        heli_1 = myAgent.get_bop(RedOprs.ZhiShengJi.Helicopter_1)
        if 'Deployment' in self.group_task:
            if heli_0:
                if heli_0['cur_hex'] == 619:
                    factionAI.task[heli_0['obj_id']] = ['ambush', 1132]  # +++
            if heli_1:
                if heli_1['cur_hex'] == 719:
                    factionAI.task[heli_1['obj_id']] = ['ambush', 1133]  # +++
        elif 'OutputFire' in self.group_task:
            if heli_0:
                factionAI.task[heli_0['obj_id']] = ['fire_propulsion']  # +++
            if heli_1:
                factionAI.task[heli_1['obj_id']] = ['fire_propulsion']  # +++


class RedWuRenJi:
    """无人机组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['wu_ren_ji']
        self.group_task = []
        self.tactic = ['Deployment']
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['wu_ren_ji']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task()

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self):
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 火力输出任务 ---
        if self.cur_step > 300:
            self.group_task = ['GuideFire']

    def assign_opr_tasks(self, factionAI, myAgent):
        drone_0 = myAgent.get_bop(RedOprs.WuRenJi.Drone_0)
        drone_1 = myAgent.get_bop(RedOprs.WuRenJi.Drone_1)
        if 'Deployment' in self.group_task:
            if drone_0:
                if drone_0['cur_hex'] == 919:
                    factionAI.task[drone_0['obj_id']] = ['maneuver', 1328]  # +++
                if drone_1['cur_hex'] == 1019:
                    factionAI.task[drone_1['obj_id']] = ['maneuver', 3110]  # +++
        elif 'GuideFire' in self.group_task:
            if drone_0:
                factionAI.task[drone_0['obj_id']] = ['guide_fire']  # +++
            if drone_1:
                factionAI.task[drone_1['obj_id']] = ['guide_fire']  # +++


class RedXunFeiDan:
    """巡飞弹组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['xun_fei_dan']
        self.group_task = []
        self.tactic = ['Deployment']
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['xun_fei_dan']['obj_ids']
        if not self.members:
            return

        # 分派巡飞弹任务
        self.assign_opr_tasks(factionAI, myAgent)

        # 计划巡飞弹的发射任务
        self.plan_launch_cm(factionAI, myAgent)

    def assign_opr_tasks(self, factionAI, myAgent):
        # === 为当前巡飞弹组的算子分派任务 ===
        for cm_id in self.members:
            cm = myAgent.get_bop(cm_id)
            if cm:
                if cm['on_board'] == 0:
                    factionAI.task[cm_id] = ['cruise_missile_2nd']

        # 制定打击蓝方步兵的计划
        blue_infantries = []
        for enemy_info in myAgent.situations.enemies:
            if not enemy_info['spread_flag']:
                continue
            enemy = enemy_info['operator']
            if enemy['type'] != BopType.Infantry:
                continue
            if np.max(enemy_info['pos_probs']) < 0.7:
                continue
            blue_infantries.append(enemy)
        if not blue_infantries:
            return
        # flag_busy_artilleries = False
        # for jm_info in myAgent.observation['jm_points']:
        #     if jm_info['color'] != factionAI.color:
        #         continue
        #     if jm_info['status'] == 0:
        #         flag_busy_artilleries = True
        #         break
        #     if jm_info['status'] == 1 and jm_info['boom_time'] <= 150:
        #         flag_busy_artilleries = True
        #         break
        # if not flag_busy_artilleries:
        tar_inf = min(blue_infantries, key=lambda x: cal_distance(2727, x['cur_hex']))
        cm_id_chosen = min(self.members,
                           key=lambda x: cal_distance(tar_inf['cur_hex'], myAgent.get_bop(x)['cur_hex']))
        factionAI.task[cm_id_chosen] = ['targeted_kill', tar_inf['obj_id']]

    def plan_launch_cm(self, factionAI, myAgent):
        # === 安排巡飞弹的下车任务 ===
        # if self.check_launch_cm(factionAI, myAgent):
        # 无条件，尽快安排发射巡飞弹
        first_choice, second_choice = None, None
        our_oprs = myAgent.get_our_oprs()
        for opr in our_oprs:
            if opr['sub_type'] == BopType.CruiseMissile:
                if opr['on_board'] == 1 and opr['get_off_remain_time'] <= 0:
                    if self.cm_getoff_check(opr['obj_id'], myAgent):
                        ifv_id = opr['car']
                        # 车上有小兵，不发射巡飞弹
                        ifv = myAgent.get_bop(ifv_id)
                        if not ifv:
                            continue
                        flag_infantry = False
                        for opr_id in ifv['passenger_ids']:
                            opr = myAgent.get_bop(opr_id)
                            if not opr:
                                continue
                            if opr['type'] == BopType.Infantry:
                                flag_infantry = True
                                break
                        if flag_infantry:
                            continue
                        if ifv_id in myAgent.observation['valid_actions'].keys():
                            if ActionType.GetOff in myAgent.observation['valid_actions'][ifv_id].keys():
                                first_choice = ifv_id
                                break
                        if not second_choice:
                            second_choice = ifv_id
        launcher_id = first_choice if first_choice else second_choice
        if launcher_id:
            cm_id = None
            for obj_id in myAgent.get_bop(launcher_id)['passenger_ids']:
                if myAgent.get_bop(obj_id)['sub_type'] == BopType.CruiseMissile:
                    cm_id = obj_id
                    break
            if cm_id:
                factionAI.task[launcher_id] = ['unload', cm_id, False]
                group_name = get_group_by_obj_id(launcher_id, factionAI)
                if group_name:
                    factionAI.group_info[group_name]['obj_ids'].remove(launcher_id)  # 暂时将战车算子调离原分组
                    if factionAI.task.get(launcher_id) and not factionAI.task_backup.get(launcher_id):
                        factionAI.task_backup[launcher_id] = factionAI.task.pop(launcher_id)

    def check_launch_cm(self, factionAI, myAgent):
        """判断是否发射巡飞弹
        判断依据：当前巡飞弹应有数量与实际数量的差距
        当前巡飞弹应有数量 = 巡飞弹生命总时长 / 游戏剩余时间
        """
        if factionAI.max_step - self.cur_step <= 1200:
            return True
        actual_amount = 0
        total_alive_time = 0
        our_oprs = myAgent.get_our_oprs()
        for opr in our_oprs:
            if opr['sub_type'] == BopType.CruiseMissile:
                if opr['on_board'] == 0:
                    actual_amount += 1
                    total_alive_time += opr['alive_remain_time']
                else:
                    total_alive_time += 1200
                    if opr['get_off_remain_time'] > 0:
                        actual_amount += 1
        goal_amount = int(total_alive_time / (factionAI.max_step - self.cur_step))
        if actual_amount < goal_amount:
            return True
        return False

    def cm_getoff_check(self, obj_id, myAgent):
        """
        确定当前巡飞弹是否可以下车
        """
        # 下车条件：找出巡飞弹的装载车辆veh，如果当前天上没有veh发射的其它巡飞弹，即可下车
        veh_id = myAgent.get_bop(obj_id)['car']
        for opr in myAgent.observation['operators']:
            if opr['sub_type'] == BopType.CruiseMissile and opr['launcher'] == veh_id:
                return False
        return True


class FactionAIBlue:
    """蓝方AI"""

    def __init__(self, myAgent):
        self.color = myAgent.color
        self.scenario = myAgent.scenario
        self.max_step = 2880
        self.tactic = None  # 我方策略，'1-attack' | '2-defend'
        self.group_info = {}  # 保存动态分组信息 {group_name: {group_task: ['ToOccupy', city], obj_ids: [opr1, opr2]}}
        self.group_static = {}  # 保存静态分组信息，用于分组动态变化后的回归
        self.init_groups()  # 初始化算子分组，分组结果整场比赛不变化
        self.task = {}  # 与是factionAI的副本，保存算子分配的任务信息{obj_id:['outputfire',param1,param2]}
        self.task_backup = {}  # 用于临时备份某些任务，方便调度
        self.last_observation = myAgent.scenario_info[-1]  # 保存上一步状态，便于和当前状态比较
        self.di_mian_2737 = BlueDiMianZu2737(self)
        self.di_mian_2941 = BlueDiMianZu2941(self)
        self.di_mian_3136 = BlueDiMianZu3136(self)
        self.di_mian_3242 = BlueDiMianZu3242(self)
        self.di_mian_3435 = BlueDiMianZu3435(self)
        self.di_mian_3829 = BlueDiMianZu3829(self)
        self.pao_bing = BluePaoBing(self)
        self.zhi_sheng_ji = BlueZhiShengJi(self)
        self.wu_ren_ji = BlueWuRenJi(self)

    def update(self, factionAI, myAgent):
        self.task = copy.deepcopy(factionAI.task)

        our_oprs = myAgent.get_our_oprs()

        # 没有可以使用的算子
        if not our_oprs:
            self.task = {}
            return

        # 如果我方有算子死亡，则将其任务删除
        our_oprs_id = [opr['obj_id'] for opr in our_oprs]
        need_pop = [opr_id for opr_id in self.task.keys() if opr_id not in our_oprs_id]
        for pop_id in need_pop:
            self.task.pop(pop_id)

        # 如果我方算子有无效任务（Success, Failure, Error等），将任务删除
        for opr in our_oprs:
            opr_id = opr['obj_id']
            if opr_id in self.task.keys():
                if self.task[opr_id][0] in ['Success', 'Failure', 'Error']:
                    self.task.pop(opr_id)

        # 将死亡算子的信息从group_info中移除
        self.remove_group_dead_members(myAgent)

        # 对我方算子进行重分组 -- 分组策略
        self.regroup_oprs(myAgent)

        # 分组任务分派
        self.di_mian_2737.plan(self, myAgent)
        self.di_mian_2941.plan(self, myAgent)
        self.di_mian_3136.plan(self, myAgent)
        self.di_mian_3242.plan(self, myAgent)
        self.di_mian_3435.plan(self, myAgent)
        self.di_mian_3829.plan(self, myAgent)
        self.pao_bing.plan(self, myAgent)
        self.zhi_sheng_ji.plan(self, myAgent)
        self.wu_ren_ji.plan(self, myAgent)

        # 应急响应
        self.hide_helicopter(myAgent)

    def init_groups(self):
        self.group_static['pao_bing'] = [BlueOprs.PaoBingQun.Artillery_0, BlueOprs.PaoBingQun.Artillery_1,
                                         BlueOprs.PaoBingQun.Artillery_2, BlueOprs.PaoBingQun.Artillery_3,
                                         BlueOprs.PaoBingQun.Artillery_4, BlueOprs.PaoBingQun.Artillery_5]
        self.group_info['pao_bing'] = {'group_task': [], 'obj_ids': self.group_static['pao_bing']}
        self.group_static['zhi_sheng_ji'] = [BlueOprs.ZhiShengJi.Helicopter_0, BlueOprs.ZhiShengJi.Helicopter_1]
        self.group_info['zhi_sheng_ji'] = {'group_task': [], 'obj_ids': self.group_static['zhi_sheng_ji']}
        self.group_static['wu_ren_ji'] = [BlueOprs.WuRenJi.Drone_0, BlueOprs.WuRenJi.Drone_1]
        self.group_info['wu_ren_ji'] = {'group_task': [], 'obj_ids': self.group_static['wu_ren_ji']}
        self.group_static['di_mian_2737'] = [BlueOprs.XianQianDui_1.Infantry_0]
        self.group_info['di_mian_2737'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_2737']}
        self.group_static['di_mian_2941'] = [BlueOprs.XianQianDui_1.Infantry_1, BlueOprs.XianQianDui_1.Tank,
                                             BlueOprs.XianQianDui_1.IFV_0, BlueOprs.XianQianDui_1.IFV_1]
        self.group_info['di_mian_2941'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_2941']}
        self.group_static['di_mian_3136'] = [BlueOprs.XianQianDui_2.Infantry_0, BlueOprs.XianQianDui_2.Tank,
                                             BlueOprs.XianQianDui_2.IFV_0, BlueOprs.XianQianDui_2.IFV_1]
        self.group_info['di_mian_3136'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_3136']}
        self.group_static['di_mian_3242'] = [BlueOprs.XianQianDui_2.Infantry_1]
        self.group_info['di_mian_3242'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_3242']}
        self.group_static['di_mian_3435'] = [BlueOprs.XianQianDui_4.Infantry_1]
        self.group_info['di_mian_3435'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_3435']}
        self.group_static['di_mian_3829'] = [BlueOprs.XianQianDui_3.Infantry_0, BlueOprs.XianQianDui_3.Infantry_1,
                                             BlueOprs.XianQianDui_3.Tank, BlueOprs.XianQianDui_3.IFV_0,
                                             BlueOprs.XianQianDui_3.IFV_1, BlueOprs.XianQianDui_4.Tank,
                                             BlueOprs.XianQianDui_4.IFV_0, BlueOprs.XianQianDui_4.IFV_1,
                                             BlueOprs.XianQianDui_4.Infantry_0]
        self.group_info['di_mian_3829'] = {'group_task': [], 'obj_ids': self.group_static['di_mian_3829']}

    def remove_group_dead_members(self, myAgent):
        """删除group_info中的已死亡算子，group_static不变化"""
        for obj_id in self.group_info['pao_bing']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['pao_bing']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['zhi_sheng_ji']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['zhi_sheng_ji']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['wu_ren_ji']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['wu_ren_ji']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_2737']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_2737']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_2941']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_2941']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_3136']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_3136']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_3242']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_3242']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_3435']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_3435']['obj_ids'].remove(obj_id)
        for obj_id in self.group_info['di_mian_3829']['obj_ids']:
            if not myAgent.get_bop(obj_id):
                self.group_info['di_mian_3829']['obj_ids'].remove(obj_id)

    def regroup_oprs(self, myAgent):
        """重新分组"""
        """=== 空闲算子按照group_static进行分组 ==="""
        our_oprs = myAgent.get_our_oprs()
        for opr in our_oprs:
            opr_id = opr['obj_id']
            regroup_flag = False
            if opr_id in self.task.keys():
                if self.task[opr_id][0] in ['Success', 'Failure', 'Error'] and opr['on_board'] == 0:
                    regroup_flag = True
            else:
                if opr['on_board'] == 0:
                    regroup_flag = True
            if regroup_flag:
                group_name = None
                for name, obj_ids in self.group_static.items():
                    if opr_id in obj_ids:
                        group_name = name
                if group_name:
                    if opr_id not in self.group_info[group_name]['obj_ids']:
                        self.group_info[group_name]['obj_ids'].append(opr_id)
                if self.task_backup.get(opr_id):  # 如果空闲算子在self.task_backup中有备份，继续备份中的任务
                    self.task[opr_id] = self.task_backup.pop(opr_id)

        """=== 调整分组 ==="""
        # === 如果2737被攻陷，删除di_mian_2941的分组信息 ===
        if not self.group_static['di_mian_2737']:
            flag_lost_2737 = True
            for obj_id in self.group_static['di_mian_2737']:
                obj = myAgent.get_bop(obj_id)
                if obj:
                    flag_lost_2737 = False
                    break
            if flag_lost_2737:
                self.group_static['di_mian_2737'] = []
                self.group_info['di_mian_2737'] = {'group_task': [], 'obj_ids': []}
        # === 如果2941被攻陷，di_mian_2941的残余兵力转守3242 ===
        if not self.group_static['di_mian_2941']:
            flag_lost_2941 = True
            remain_members = []
            for obj_id in self.group_static['di_mian_2941']:
                obj = myAgent.get_bop(obj_id)
                if obj:
                    remain_members.append(obj_id)
                    if obj['type'] == BopType.Infantry:
                        flag_lost_2941 = False
            if flag_lost_2941:
                self.group_static['di_mian_3242'] += remain_members
                for obj_id in remain_members:
                    if obj_id in self.group_info['di_mian_2941']['obj_ids']:
                        self.group_info['di_mian_3242']['obj_ids'].append(obj_id)
                self.group_static['di_mian_2941'] = []
        # === 如果3136被攻陷，di_mian_3136的残余兵力转守3242 ===
        if not self.group_static['di_mian_3136']:
            flag_lost_3136 = True
            remain_members = []
            for obj_id in self.group_static['di_mian_3136']:
                obj = myAgent.get_bop(obj_id)
                if obj:
                    remain_members.append(obj_id)
                    if obj['type'] == BopType.Infantry:
                        flag_lost_3136 = False
            if flag_lost_3136:
                self.group_static['di_mian_3242'] += remain_members
                for obj_id in remain_members:
                    if obj_id in self.group_info['di_mian_3136']['obj_ids']:
                        self.group_info['di_mian_3242']['obj_ids'].append(obj_id)
                self.group_static['di_mian_3136'] = []

    def hide_helicopter(self, myAgent):
        # 遭到直升机突袭的反应策略
        map_size = myAgent.get_map_size()
        n_hexes = map_size['row'] * map_size['col']
        dist_matrix = myAgent.my_map['dist_matrix']
        enemy_heli_0 = myAgent.get_bop(RedOprs.ZhiShengJi.Helicopter_0)
        enemy_heli_1 = myAgent.get_bop(RedOprs.ZhiShengJi.Helicopter_1)
        if not enemy_heli_0 and not enemy_heli_1:
            return
        enemy_helis = []
        if enemy_heli_0:
            enemy_helis.append(enemy_heli_0)
        if enemy_heli_1:
            enemy_helis.append(enemy_heli_1)
        # 计算直升机的威胁范围
        threat_area_vehicle_bool = np.zeros(n_hexes, dtype=bool)
        shoot_area_infantry_bool = np.zeros(n_hexes, dtype=bool)
        for heli in enemy_helis:
            heli_info = myAgent.get_enemy_info(heli['obj_id'])
            heli_loc_ind = mapid_2_matind(heli['cur_hex'], map_size)
            threat_area_vehicle_bool = np.logical_or(threat_area_vehicle_bool,
                                                     get_opr_shoot_range(heli, BopType.Vehicle, myAgent,
                                                                         heli['carry_weapon_ids'],
                                                                         heli_info['remain_bullet_nums']))
            shoot_area_infantry_bool = np.logical_or(shoot_area_infantry_bool, dist_matrix[heli_loc_ind] <= 10)
        threat_area_vehicle = list(matind_2_mapid(np.arange(n_hexes)[threat_area_vehicle_bool], map_size))
        shoot_area_infantry = list(matind_2_mapid(np.arange(n_hexes)[shoot_area_infantry_bool], map_size))
        for opr in myAgent.get_our_oprs():
            if opr['type'] == BopType.Infantry:
                """步兵的反应：停下来准备射击"""
                if opr['remain_bullet_nums'][BulletType.HeavyMissile] <= 0:
                    continue
                if opr['cur_hex'] not in shoot_area_infantry:
                    continue
                ambush_loc = opr['move_path'][-1] if opr['move_path'] else opr['cur_hex']
                if self.task.get(opr['obj_id']):
                    if 'air_defense' not in self.task[opr['obj_id']] and not self.task_backup.get(opr['obj_id']):  # 本策略中'air_defense'的任务只用于步兵打直升机
                        self.task_backup[opr['obj_id']] = self.task.pop(opr['obj_id'])
                    self.task[opr['obj_id']] = ['air_defense', ambush_loc]
            elif opr['type'] == BopType.Vehicle:
                """车辆的反应：能逃就逃"""
                if opr['cur_hex'] not in threat_area_vehicle:
                    continue
                if self.task.get(opr['obj_id']):
                    if 'hide' not in self.task[opr['obj_id']] and not self.task_backup.get(opr['obj_id']):
                        self.task_backup[opr['obj_id']] = self.task.pop(opr['obj_id'])
                    self.task[opr['obj_id']] = ['hide']


class BlueDiMianZu2737:
    """地面组2737的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_2737']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_2737']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, myAgent):
        infantry_0 = myAgent.get_bop(BlueOprs.XianQianDui_1.Infantry_0)
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 防守任务 ---
        if infantry_0['cur_hex'] == 2736 and 'Deployment' in self.group_task:
            self.group_task = ['Defend', 2737]

    def assign_opr_tasks(self, factionAI, myAgent):
        infantry_0 = myAgent.get_bop(BlueOprs.XianQianDui_1.Infantry_0)
        if 'Deployment' in self.group_task:
            if infantry_0:
                if infantry_0['cur_hex'] == 2639:
                    factionAI.task[infantry_0['obj_id']] = ['maneuver', 2737]  # +++
                if infantry_0['cur_hex'] == 2737:
                    factionAI.task[infantry_0['obj_id']] = ['ambush2nd', 2736, True]  # +++
        elif 'Defend' in self.group_task:
            defend_city_loc = self.group_task[1]
            # 确定防守方向
            enemy_directions = []
            for enemy_info in myAgent.situations.enemies:
                if enemy_info['spread_flag']:
                    enemy = enemy_info['operator']
                    if enemy['sub_type'] == BopType.Tank:
                        if 0 < cal_distance(defend_city_loc, enemy['cur_hex']) < 25:
                            direction = get_direction(defend_city_loc, enemy['cur_hex'])
                            if isinstance(direction, int) and direction not in enemy_directions:
                                enemy_directions.append(direction)
            if 4 not in enemy_directions:
                enemy_directions.append(4)
            if infantry_0:
                factionAI.task[infantry_0['obj_id']] = ['defend_firepower', defend_city_loc, enemy_directions]


class BlueDiMianZu2941:
    """地面组2941的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_2941']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_2941']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 防守任务 ---
        if not factionAI.group_static['di_mian_2737'] and 'Deployment' in self.group_task:
            self.group_task = ['Defend', 2941]

    def assign_opr_tasks(self, factionAI, myAgent):
        if 'Deployment' in self.group_task:
            infantry_1, tank = myAgent.get_bop(BlueOprs.XianQianDui_1.Infantry_1), myAgent.get_bop(
                BlueOprs.XianQianDui_1.Tank)
            ifv_0, ifv_1 = myAgent.get_bop(BlueOprs.XianQianDui_1.IFV_0), myAgent.get_bop(BlueOprs.XianQianDui_1.IFV_1)
            if infantry_1:
                if infantry_1['cur_hex'] == 2640 and self.cur_step <= 10:
                    factionAI.task[infantry_1['obj_id']] = ['maneuver', 2941]  # +++
                if infantry_1['cur_hex'] == 2941:
                    factionAI.task[infantry_1['obj_id']] = ['ambush4th', 2841, True]  # +++
            if tank:
                if tank['cur_hex'] == 2739 and self.cur_step <= 10:
                    factionAI.task[tank['obj_id']] = ['ambush4th', 2643, True]  # +++
            if ifv_0:
                if ifv_0['cur_hex'] == 2639 and self.cur_step <= 10:
                    factionAI.task[ifv_0['obj_id']] = ['ambush4th', 2846, True]  # +++
            if ifv_1:
                if ifv_1['cur_hex'] == 2640 and self.cur_step <= 10:
                    factionAI.task[ifv_1['obj_id']] = ['ambush4th', 2945, True]
        elif 'Defend' in self.group_task:
            defend_city_loc = self.group_task[1]
            # 确定防守方向
            enemy_directions = []
            for enemy_info in myAgent.situations.enemies:
                if enemy_info['spread_flag']:
                    enemy = enemy_info['operator']
                    if enemy['sub_type'] == BopType.Tank:
                        if 0 < cal_distance(defend_city_loc, enemy['cur_hex']) < 25:
                            direction = get_direction(defend_city_loc, enemy['cur_hex'])
                            if isinstance(direction, int) and direction not in enemy_directions:
                                enemy_directions.append(direction)
            if 4 not in enemy_directions:
                enemy_directions.append(4)
            for obj_id in self.members:
                obj = myAgent.get_bop(obj_id)
                if obj:
                    factionAI.task[obj_id] = ['defend_firepower', defend_city_loc, enemy_directions]  # +++


class BlueDiMianZu3136:
    """地面组3136的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_3136']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_3136']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 防守任务 ---
        if not factionAI.group_static['di_mian_2737'] and 'Deployment' in self.group_task:
            self.group_task = ['Defend', 3136]

    def assign_opr_tasks(self, factionAI, myAgent):
        if 'Deployment' in self.group_task:
            infantry_0, tank = myAgent.get_bop(BlueOprs.XianQianDui_2.Infantry_0), myAgent.get_bop(
                BlueOprs.XianQianDui_2.Tank)
            ifv_0, ifv_1 = myAgent.get_bop(BlueOprs.XianQianDui_2.IFV_0), myAgent.get_bop(BlueOprs.XianQianDui_2.IFV_1)
            if infantry_0:
                if infantry_0['cur_hex'] == 3039 and self.cur_step <= 10:
                    factionAI.task[infantry_0['obj_id']] = ['ambush4th', 3037, True]  # +++
            if tank:
                if tank['cur_hex'] == 3139 and self.cur_step <= 10:
                    factionAI.task[tank['obj_id']] = ['ambush4th', 3137, True]  # +++
            if ifv_0:
                if ifv_0['cur_hex'] == 3039 and self.cur_step <= 10:
                    factionAI.task[ifv_0['obj_id']] = ['ambush4th', 3136, True]  # +++
            if ifv_1:
                if ifv_1['cur_hex'] == 3040 and self.cur_step <= 10:
                    factionAI.task[ifv_1['obj_id']] = ['ambush4th', 3642, True]
        elif 'Defend' in self.group_task:
            defend_city_loc = self.group_task[1]
            # 确定防守方向
            enemy_directions = []
            for enemy_info in myAgent.situations.enemies:
                if enemy_info['spread_flag']:
                    enemy = enemy_info['operator']
                    if enemy['sub_type'] == BopType.Tank:
                        if 0 < cal_distance(defend_city_loc, enemy['cur_hex']) < 25:
                            direction = get_direction(defend_city_loc, enemy['cur_hex'])
                            if isinstance(direction, int) and direction not in enemy_directions:
                                enemy_directions.append(direction)
            if 4 not in enemy_directions:
                enemy_directions.append(4)
            for obj_id in self.members:
                obj = myAgent.get_bop(obj_id)
                if obj:
                    factionAI.task[obj_id] = ['defend_firepower', defend_city_loc, enemy_directions]  # +++


class BlueDiMianZu3242:
    """地面组3242的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_3242']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_3242']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        infantry_1 = myAgent.get_bop(BlueOprs.XianQianDui_2.Infantry_1)
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 防守任务 ---
        if infantry_1['cur_hex'] == 3142 and 'Deployment' in self.group_task:
            self.group_task = ['Defend', 3242]

    def assign_opr_tasks(self, factionAI, myAgent):
        if 'Deployment' in self.group_task:
            infantry_1 = myAgent.get_bop(BlueOprs.XianQianDui_2.Infantry_1)
            if infantry_1:
                if infantry_1['cur_hex'] == 3040 and self.cur_step <= 10:
                    factionAI.task[infantry_1['obj_id']] = ['maneuver', 3242]
                if infantry_1['cur_hex'] == 3242:
                    factionAI.task[infantry_1['obj_id']] = ['ambush4th', 3142, True]  # +++
        elif 'Defend' in self.group_task:
            defend_city_loc = self.group_task[1]
            # 确定防守方向
            enemy_directions = []
            for enemy_info in myAgent.situations.enemies:
                if enemy_info['spread_flag']:
                    enemy = enemy_info['operator']
                    if enemy['sub_type'] == BopType.Tank:
                        if 0 < cal_distance(defend_city_loc, enemy['cur_hex']) < 25:
                            direction = get_direction(defend_city_loc, enemy['cur_hex'])
                            if isinstance(direction, int) and direction not in enemy_directions:
                                enemy_directions.append(direction)
            if 4 not in enemy_directions:
                enemy_directions.append(4)
            for obj_id in self.members:
                obj = myAgent.get_bop(obj_id)
                if obj:
                    factionAI.task[obj_id] = ['defend_firepower', defend_city_loc, enemy_directions]  # +++


class BlueDiMianZu3435:
    """地面组3435的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_3435']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_3435']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        infantry_1 = myAgent.get_bop(BlueOprs.XianQianDui_4.Infantry_1)
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 防守任务 ---
        if infantry_1['cur_hex'] == 3535 and 'Deployment' in self.group_task:
            self.group_task = ['Defend', 3435]

    def assign_opr_tasks(self, factionAI, myAgent):
        if 'Deployment' in self.group_task:
            infantry_1 = myAgent.get_bop(BlueOprs.XianQianDui_4.Infantry_1)
            if infantry_1:
                if infantry_1['cur_hex'] == 4130 and self.cur_step <= 10:
                    factionAI.task[infantry_1['obj_id']] = ['maneuver', 3435]
                if infantry_1['cur_hex'] == 3435:
                    factionAI.task[infantry_1['obj_id']] = ['ambush4th', 3535, True]  # +++
        elif 'Defend' in self.group_task:
            defend_city_loc = self.group_task[1]
            # 确定防守方向
            enemy_directions = []
            for enemy_info in myAgent.situations.enemies:
                if enemy_info['spread_flag']:
                    enemy = enemy_info['operator']
                    if enemy['sub_type'] == BopType.Tank:
                        if 0 < cal_distance(defend_city_loc, enemy['cur_hex']) < 25:
                            direction = get_direction(defend_city_loc, enemy['cur_hex'])
                            if isinstance(direction, int) and direction not in enemy_directions:
                                enemy_directions.append(direction)
            if 4 not in enemy_directions:
                enemy_directions.append(4)
            if 3 not in enemy_directions:
                enemy_directions.append(3)
            for obj_id in self.members:
                obj = myAgent.get_bop(obj_id)
                if obj:
                    factionAI.task[obj_id] = ['defend_firepower', defend_city_loc, enemy_directions]  # +++


class BlueDiMianZu3829:
    """地面组3829的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['di_mian_3829']
        self.group_task = ['Deployment']
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['di_mian_3829']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 防守任务 ---
        if self.cur_step >= 400 and 'Deployment' in self.group_task:
            flag_defend = False
            for enemy_info in myAgent.situations.enemies:
                if enemy_info['spread_flag']:
                    enemy = enemy_info['operator']
                    if enemy['sub_type'] in [BopType.Tank, BopType.IFV, BopType.Man]:
                        if cal_distance(enemy['cur_hex'], 3829) <= 25:
                            flag_defend = True
            if flag_defend:
                self.group_task = ['Defend', 3829]

    def assign_opr_tasks(self, factionAI, myAgent):
        if 'Deployment' in self.group_task:
            infantry_0_3 = myAgent.get_bop(BlueOprs.XianQianDui_3.Infantry_0)
            infantry_1_3 = myAgent.get_bop(BlueOprs.XianQianDui_3.Infantry_1)
            tank_3, ifv_0_3 = myAgent.get_bop(BlueOprs.XianQianDui_3.Tank), myAgent.get_bop(
                BlueOprs.XianQianDui_3.IFV_0)
            ifv_1_3, tank_4 = myAgent.get_bop(BlueOprs.XianQianDui_3.IFV_1), myAgent.get_bop(
                BlueOprs.XianQianDui_4.Tank)
            ifv_0_4, ifv_1_4 = myAgent.get_bop(BlueOprs.XianQianDui_4.IFV_0), myAgent.get_bop(
                BlueOprs.XianQianDui_4.IFV_1)
            infantry_0_4 = myAgent.get_bop(BlueOprs.XianQianDui_4.Infantry_0)
            if infantry_0_3:
                if infantry_0_3['cur_hex'] == 4027 and self.cur_step <= 10:
                    factionAI.task[infantry_0_3['obj_id']] = ['ambush4th', 3828, True]  # +++
            if infantry_1_3:
                if infantry_1_3['cur_hex'] == 4028 and self.cur_step <= 10:
                    factionAI.task[infantry_1_3['obj_id']] = ['ambush4th', 4128, True]  # +++
            if tank_3:
                if tank_3['cur_hex'] == 4127 and self.cur_step <= 10:
                    factionAI.task[tank_3['obj_id']] = ['maneuver', 3829]  # +++
                if tank_3['cur_hex'] == 3829:
                    factionAI.task[tank_3['obj_id']] = ['ambush4th', 3928, True]  # +++
            if tank_4:
                if tank_4['cur_hex'] == 4230:
                    factionAI.task[tank_4['obj_id']] = ['ambush4th', 4028, True]  # +++
            if ifv_0_3:
                if ifv_0_3['cur_hex'] == 4027 and self.cur_step <= 10:
                    factionAI.task[ifv_0_3['obj_id']] = ['ambush4th', 4327, True]  # +++
            if ifv_1_3:
                if ifv_1_3['cur_hex'] == 4028 and self.cur_step <= 10:
                    factionAI.task[ifv_1_3['obj_id']] = ['ambush4th', 4030, True]  # +++
            if ifv_0_4:
                if infantry_0_4:
                    if ifv_0_4['cur_hex'] == 4129 and infantry_0_4['get_on_remain_time'] <= 0 and infantry_0_4[
                        'on_board'] == 0:
                        factionAI.task[ifv_0_4['obj_id']] = ['deliver', infantry_0_4['obj_id'], 4631]  # +++
                    if ifv_0_4['cur_hex'] == 4631 and infantry_0_4['get_off_remain_time'] > 0:
                        factionAI.task[ifv_0_4['obj_id']] = ['ambush4th', 4530, True]  # +++
                        factionAI.task[infantry_0_4['obj_id']] = ['ambush4th', 4631, True]  # +++
            if ifv_1_4:
                if ifv_1_4['cur_hex'] == 4130 and self.cur_step <= 10:
                    factionAI.task[ifv_1_3['obj_id']] = ['ambush4th', 4836, True]  # +++
        elif 'Defend' in self.group_task:
            defend_city_loc = self.group_task[1]
            # 确定防守方向
            enemy_directions = []
            for enemy_info in myAgent.situations.enemies:
                if enemy_info['spread_flag']:
                    enemy = enemy_info['operator']
                    if enemy['sub_type'] == BopType.Tank:
                        if 0 < cal_distance(defend_city_loc, enemy['cur_hex']) < 25:
                            direction = get_direction(defend_city_loc, enemy['cur_hex'])
                            if isinstance(direction, int) and direction not in enemy_directions:
                                enemy_directions.append(direction)
            if 3 not in enemy_directions:
                enemy_directions.append(3)
            if 5 not in enemy_directions:
                enemy_directions.append(5)
            for obj_id in self.members:
                if obj_id == BlueOprs.XianQianDui_3.Infantry_1:
                    factionAI.task[obj_id] = ['defend_firepower', 3630, enemy_directions]  # +++
                    continue
                if obj_id == BlueOprs.XianQianDui_4.Infantry_0:
                    factionAI.task[obj_id] = ['ambush4th', 4631, True]  # +++
                    continue
                obj = myAgent.get_bop(obj_id)
                if obj:
                    factionAI.task[obj_id] = ['defend_firepower', defend_city_loc, enemy_directions]  # +++


class BluePaoBing:
    """炮兵组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['pao_bing']
        self.group_task = []
        self.tactic = None
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['pao_bing']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        """炮兵组任务"""
        """=== 清空炮兵组任务 ==="""
        flag_task_empty = True
        for arti_id in self.members:
            arti = myAgent.get_bop(arti_id)
            if arti:
                if arti_id in factionAI.task.keys():
                    flag_task_empty = False
                    break
        if flag_task_empty:
            self.group_task = []

        """=== 删除死亡的校射算子 ==="""
        if self.group_task:
            if isinstance(self.group_task[2], int):
                directing_opr_id = self.group_task[2]
                if not myAgent.get_bop(directing_opr_id):
                    self.group_task[2] = None

        """=== 关键点位压制任务 ==="""
        if self.new_plan_check(myAgent):
            if self.cur_step <= 150:
                self.group_task = ['FireSupport', 1312, None]  # 第1波压制
                return
            elif self.cur_step <= 450:
                self.group_task = ['FireSupport', 1228, None]  # 第2波压制
                return
            elif self.cur_step <= 750:
                self.group_task = ['FireSupport', 3210, None]  # 第3波压制
                return

        """=== 常规任务 ==="""
        if self.cur_step <= 750:
            return
        if not self.new_plan_check(myAgent):
            return
        # --- 计算飞行时间 ---
        fly_time = 0
        for jm_info in myAgent.observation['jm_points']:
            if jm_info['obj_id'] in self.members:
                if jm_info['fly_time'] > fly_time:
                    fly_time = jm_info['fly_time']
        if fly_time > 100:  # 快要达到的火力不打断
            return
        # --- 确定炮兵目标 ---
        being_jm_hexes = []  # 正在被间瞄损伤的六角格
        for jm_info in myAgent.observation['jm_points']:
            if jm_info['status'] == 0:  # 正在飞行
                being_jm_hexes.append(jm_info['pos'])
        being_jm_hexes = list(set(being_jm_hexes))
        being_planed_hexes = []  # 在计划中的间瞄目标
        if self.group_task:
            being_planed_hexes.append(self.group_task[1])
        near_by_hexes = []  # 我方算子周边格
        for opr in myAgent.get_our_oprs():
            if opr['type'] != BopType.Aircraft:
                opr_loc = opr['cur_hex']
                near_by_hexes += get_range(opr_loc, 1, myAgent.get_map_size())
        near_by_hexes = list(set(near_by_hexes))
        targets = get_artillery_target(myAgent)
        for key, value in targets.items():  # 过滤正在遭受打击的目标，以及列入间瞄任务的目标
            if not value:
                for tar_id in value:
                    tar_loc = myAgent.get_bop(tar_id)['cur_hex']
                    if tar_loc in being_jm_hexes + being_planed_hexes + near_by_hexes:
                        targets[key].remove(tar_id)
        # --- 有受压制的步兵时，优先打击此类目标 ---
        if targets[0]:
            target = targets[0][0]
            if self.group_task:
                cur_tar_loc = self.group_task[1]
                if check_keep_infantry_in_hex(cur_tar_loc, myAgent.situations.enemies):  # 当前的打击目标就是压制步兵
                    return
            tar_loc = myAgent.get_enemy(target)['cur_hex']
            self.group_task = ['FireSupport', tar_loc, None]
        elif not self.group_task:
            # === 没有受压制的步兵时，根据空闲的炮兵组，按照优先顺序分派jm任务 ===
            # 筛选炮击目标六角格
            candidate_targets = None
            for m in range(1, 4):
                if targets[m]:
                    candidate_targets = targets[m]
                    break
            if candidate_targets:
                arti_locs = []
                for arti_id in self.members:
                    arti = myAgent.get_bop(arti_id)
                    if arti:
                        arti_locs.append(arti['cur_hex'])
                candidate_locs = [myAgent.get_enemy_info(obj_id)['operator']['cur_hex'] for obj_id in
                                  candidate_targets]
                candidate_locs = list(set(candidate_locs))
                tar_loc = get_min_dist_loc(candidate_locs, arti_locs, myAgent)
                self.group_task = ['FireSupport', tar_loc, None]

        """=== 为炮兵组任务分配校射算子 ==="""
        if self.cur_step > 750:
            if not self.group_task:
                return
            if isinstance(self.group_task[2], int):
                return
            fly_time = 0
            for jm_info in myAgent.observation['jm_points']:
                if jm_info['obj_id'] in self.members:
                    if jm_info['fly_time'] > fly_time:
                        fly_time = jm_info['fly_time']
            if fly_time > 50:  # 只为早期任务分配校射算子
                return
            # 筛选校射算子
            tar_loc = self.group_task[1]
            directing_opr_id = self.get_directing_opr(tar_loc, myAgent)
            if directing_opr_id:
                self.group_task[2] = directing_opr_id  # 完善炮兵组任务
                group_name = get_group_by_obj_id(directing_opr_id, factionAI)
                if group_name:
                    factionAI.group_info[group_name]['obj_ids'].remove(directing_opr_id)  # 暂时将校射算子调离原分组
                    if factionAI.task.get(directing_opr_id) and not factionAI.task_backup.get(directing_opr_id):
                        factionAI.task_backup[directing_opr_id] = factionAI.task.pop(directing_opr_id)

    def assign_opr_tasks(self, factionAI, myAgent):
        """为算子分派任务"""
        """=== 清除完成的校射任务 ==="""
        if self.group_task:
            if isinstance(self.group_task[2], int):
                directing_opr_ids = [self.group_task[2]]
            else:
                directing_opr_ids = []
            for opr in myAgent.get_our_oprs():
                obj_id = opr['obj_id']
                if obj_id not in factionAI.task.keys():
                    continue
                if 'direct_artilleries' in factionAI.task[obj_id]:
                    if not directing_opr_ids:
                        factionAI.task.pop(obj_id)
                    else:
                        if obj_id not in directing_opr_ids:
                            factionAI.task.pop(obj_id)

        """=== 分派炮兵齐射任务 ==="""
        if self.group_task:
            for arti_id in self.members:
                factionAI.task[arti_id] = ['jm_salvo', self.group_task[1]]

        """=== 分派校射任务 ==="""
        if self.group_task:
            if isinstance(self.group_task[2], int):
                directing_opr_id = self.group_task[2]
                if directing_opr_id in factionAI.task.keys():
                    if factionAI.task[directing_opr_id] == ['direct_artilleries', self.group_task[1]]:
                        return
                factionAI.task[directing_opr_id] = ['direct_artilleries', self.group_task[1]]

    def get_directing_opr(self, tar_loc, myAgent):
        """为炮兵组选择校射算子"""
        # === 确定对tar_enemies的可视半径 ===
        observe_radius = {}  # 最小可观察范围
        tar_loc_terrain = get_terrain_info(myAgent.map.get_map_data(), tar_loc)
        if tar_loc_terrain in [TerrainType.Forest, TerrainType.Resident]:
            terrain_flag = True
        else:
            terrain_flag = False
        if terrain_flag:
            observe_radius[BopType.Infantry] = 12
            observe_radius[BopType.Vehicle] = 12
            observe_radius[BopType.Drone] = 1
        else:
            observe_radius[BopType.Infantry] = 25
            observe_radius[BopType.Vehicle] = 25
            observe_radius[BopType.Drone] = 2
        # === 计算对tar_enemies的最小观察区域 ===
        dist_matrix = myAgent.my_map['dist_matrix']
        map_size = myAgent.get_map_size()
        n_hexes = map_size['row'] * map_size['col']
        tar_loc_ind = mapid_2_matind(tar_loc, myAgent.get_map_size())
        dist_vec = dist_matrix[tar_loc_ind]
        see_vec_ground = myAgent.my_map['see_matrix'][0][tar_loc_ind]
        see_vec_air = myAgent.my_map['see_matrix'][2][tar_loc_ind]
        observe_area = {}
        for bop_type, radius in observe_radius.items():
            if bop_type in [BopType.Infantry, BopType.Vehicle]:
                observe_area[bop_type] = np.arange(n_hexes)[dist_vec <= observe_radius[bop_type] & see_vec_ground]
            else:
                observe_area[bop_type] = np.arange(n_hexes)[dist_vec <= observe_radius[bop_type] & see_vec_air]
        # === 筛选出能够在150s内抵达最小观察区域的我方算子 ===
        cost_matrix_vehicle = myAgent.my_map['cost_matrix'][0]
        cost_matrix_infantry = myAgent.my_map['cost_matrix'][1]
        cost_matrix_little_aircraft = dist_matrix * 7.2
        candidate_oprs = []
        for opr in myAgent.get_our_oprs():
            if opr['sub_type'] not in [BopType.Artillery, BopType.CruiseMissile, BopType.Helicopter]:
                opr_loc_ind = mapid_2_matind(opr['cur_hex'], map_size)
                if opr['type'] == BopType.Infantry:
                    cost_vec = cost_matrix_infantry[opr_loc_ind]
                    if np.any(cost_vec[observe_area[BopType.Infantry]] < 150):
                        candidate_oprs.append(opr)
                elif opr['type'] == BopType.Vehicle:
                    cost_vec = cost_matrix_vehicle[opr_loc_ind]
                    if np.any(cost_vec[observe_area[BopType.Vehicle]] < 150):
                        candidate_oprs.append(opr)
                elif opr['sub_type'] == BopType.Drone:
                    cost_vec = cost_matrix_little_aircraft[opr_loc_ind]
                    if np.any(cost_vec[observe_area[BopType.Drone]] < 150):
                        candidate_oprs.append(opr)
        # 进一步从candidate_oprs筛选出最优的校射算子
        if not candidate_oprs:
            return None
        tar_enemies = []
        for enemy_info in myAgent.situations.enemies:
            if enemy_info['spread_flag']:
                enemy = enemy_info['operator']
                ene_loc = enemy['cur_hex']
                if enemy['type'] != BopType.Aircraft:
                    if enemy['armor'] != ArmorType.Composit:
                        if ene_loc == tar_loc:
                            if np.max(enemy_info['pos_probs']) > 1 - 1e-8:
                                tar_enemies.append(enemy)
        if not tar_enemies:
            return None
        infantry_flag = False  # 确定是否有步兵算子
        for enemy in tar_enemies:
            if enemy['type'] == BopType.Infantry:
                infantry_flag = True
                break
        drone_oprs, vehicle_oprs, infantry_oprs = [], [], []
        for opr in candidate_oprs:
            if opr['sub_type'] == BopType.Drone:
                drone_oprs.append(opr)
            elif opr['type'] == BopType.Vehicle:
                vehicle_oprs.append(opr)
            else:
                infantry_oprs.append(opr)
        poss_oprs = None
        if infantry_flag:  # 如果有步兵
            keep_flag = True  # 步兵算子是否处于压制状态
            for enemy in tar_enemies:
                if enemy['type'] == BopType.Infantry and enemy['keep'] == 0:
                    keep_flag = False
                    break
            if keep_flag:  # 步兵算子处于压制状态时的校射算子筛选
                if infantry_oprs:
                    poss_oprs = infantry_oprs
                elif drone_oprs:
                    poss_oprs = drone_oprs
                elif vehicle_oprs:
                    poss_oprs = vehicle_oprs
            else:  # 步兵算子没有处于压制状态的情况
                if infantry_oprs:
                    poss_oprs = infantry_oprs
                elif vehicle_oprs:
                    poss_oprs = vehicle_oprs
        else:  # 没有步兵算子
            if drone_oprs:
                poss_oprs = drone_oprs
            elif infantry_oprs:
                poss_oprs = infantry_oprs
            elif vehicle_oprs:
                poss_oprs = vehicle_oprs
        best_opr_id = None
        if poss_oprs:
            min_dist = 1000
            for opr in poss_oprs:
                dist = myAgent.map.get_distance(opr['cur_hex'], tar_loc)
                if min_dist > dist:
                    min_dist = dist
                    best_opr_id = opr['obj_id']
        return best_opr_id

    def new_plan_check(self, myAgent):
        for anti_id in self.members:
            if myAgent.observation['valid_actions'].get(anti_id):
                if not myAgent.observation['valid_actions'][anti_id].get(ActionType.JMPlan):
                    return False
        return True


class BlueZhiShengJi:
    """直升机组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['zhi_sheng_ji']
        self.group_task = []
        self.tactic = ['Deployment']
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['zhi_sheng_ji']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(factionAI, myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, factionAI, myAgent):
        heli_0 = myAgent.get_bop(BlueOprs.ZhiShengJi.Helicopter_0)
        heli_1 = myAgent.get_bop(BlueOprs.ZhiShengJi.Helicopter_1)
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 火力输出任务 ---
        if heli_0 and heli_1:
            if heli_0['cur_hex'] == 1429 and heli_1['cur_hex'] == 3114 and 'Deployment' in self.group_task:
                self.group_task = ['OutputFire']

    def assign_opr_tasks(self, factionAI, myAgent):
        heli_0 = myAgent.get_bop(BlueOprs.ZhiShengJi.Helicopter_0)
        heli_1 = myAgent.get_bop(BlueOprs.ZhiShengJi.Helicopter_1)
        if 'Deployment' in self.group_task:
            if heli_0:
                if heli_0['cur_hex'] == 5557:
                    factionAI.task[heli_0['obj_id']] = ['ambush', 1429]  # +++
            if heli_1:
                if heli_1['cur_hex'] == 5558:
                    factionAI.task[heli_1['obj_id']] = ['ambush', 3114]  # +++
        elif 'OutputFire' in self.group_task:
            if heli_0:
                factionAI.task[heli_0['obj_id']] = ['fire_propulsion']  # +++
            if heli_1:
                factionAI.task[heli_1['obj_id']] = ['fire_propulsion']  # +++


class BlueWuRenJi:
    """无人机组的策略实现"""

    def __init__(self, factionAI):
        self.members = factionAI.group_static['wu_ren_ji']
        self.group_task = []
        self.tactic = ['Deployment']
        self.cur_step = 0

    def plan(self, factionAI, myAgent):
        self.cur_step = myAgent.observation['time']['cur_step']
        self.members = factionAI.group_info['wu_ren_ji']['obj_ids']
        if not self.members:
            return

        # === 更新组任务 ===
        self.update_group_task(myAgent)

        # === 分派算子任务 ===
        self.assign_opr_tasks(factionAI, myAgent)

    def update_group_task(self, myAgent):
        drone_0 = myAgent.get_bop(BlueOprs.WuRenJi.Drone_0)
        drone_1 = myAgent.get_bop(BlueOprs.WuRenJi.Drone_1)
        # --- 部署任务 ---
        if not self.group_task and self.cur_step < 10:
            self.group_task = ['Deployment']
        # --- 火力输出任务 ---
        if drone_0 and drone_1:
            if drone_0['cur_hex'] == 1829 and drone_1['cur_hex'] == 3118 and 'Deployment' in self.group_task:
                self.group_task = ['Scout']

    def assign_opr_tasks(self, factionAI, myAgent):
        drone_0 = myAgent.get_bop(BlueOprs.WuRenJi.Drone_0)
        drone_1 = myAgent.get_bop(BlueOprs.WuRenJi.Drone_1)
        if 'Deployment' in self.group_task:
            if drone_0:
                if drone_0['cur_hex'] == 5657:
                    factionAI.task[drone_0['obj_id']] = ['maneuver', 1829]  # +++
                if drone_1['cur_hex'] == 5658:
                    factionAI.task[drone_1['obj_id']] = ['maneuver', 3118]  # +++
        elif 'Scout' in self.group_task:
            if drone_0:
                factionAI.task[drone_0['obj_id']] = ['drone_scout']  # +++
            if drone_1:
                factionAI.task[drone_1['obj_id']] = ['drone_scout']  # +++


def get_artillery_target(myAgent):
    """2020-6-25
    确定炮兵的目标
    """
    targets = {0: [], 1: [], 2: [], 3: []}  # 按级别区分的目标
    # 0：压制的步兵
    # 1：无掩蔽、无地形保护的步兵（有侦察单位)
    # 2：无掩蔽、无地形保护的步兵（无侦察单位)，其它步兵（有侦察单位），无掩蔽、无地形保护的车辆
    # 3： 其它步兵（无侦察单位），其它车辆
    # 确定可用的校射算子类型
    cm_flag, drone_flag, heli_flag = False, False, False
    our_oprs = myAgent.get_our_oprs()
    for opr in our_oprs:
        if opr['sub_type'] == BopType.CruiseMissile and opr['on_board'] == 0:
            cm_flag = True
        if opr['sub_type'] == BopType.Drone:
            drone_flag = True
        if opr['sub_type'] == BopType.Helicopter:
            heli_flag = True
    # 区分目标等级
    for enemy_info in myAgent.situations.enemies:
        enemy = enemy_info['operator']
        ene_id = enemy['obj_id']
        ene_loc = enemy['cur_hex']
        if enemy['type'] == BopType.Aircraft:
            continue
        if enemy['armor'] == ArmorType.Composit:
            continue
        if np.max(enemy_info['pos_probs']) < 1 - 1e-8:
            continue
        if enemy['stop'] == 0:  # 算子没有处于静止状态
            continue
        if enemy['type'] == BopType.Infantry:
            if enemy['keep'] == 1:
                targets[0].append(ene_id)  # 最高等级是压制的步兵
            elif cm_flag:
                targets[1].append(ene_id)
            elif drone_flag:
                terrain = get_terrain_info(myAgent.map.get_map_data(), ene_loc)
                if enemy['move_state'] != MoveState.Shelter and terrain not in [TerrainType.Forest,
                                                                                TerrainType.Resident]:
                    targets[1].append(ene_id)
                elif enemy_info['remain_bullet_nums'][PortableMissile().type] <= 0:  # 没有导弹就没有对空威胁
                    targets[2].append(ene_id)
                else:
                    targets[3].append(ene_id)
            elif heli_flag:
                terrain = get_terrain_info(myAgent.map.get_map_data(), ene_loc)
                if enemy_info['remain_bullet_nums'][PortableMissile().type] <= 0:  # 没有导弹就没有对空威胁
                    if enemy['move_state'] != MoveState.Shelter and terrain not in [TerrainType.Forest,
                                                                                    TerrainType.Resident]:
                        targets[1].append(ene_id)
                    else:
                        targets[2].append(ene_id)
                else:
                    if enemy['move_state'] != MoveState.Shelter and terrain not in [TerrainType.Forest,
                                                                                    TerrainType.Resident]:
                        targets[2].append(ene_id)
                    else:
                        targets[3].append(ene_id)
            else:
                terrain = get_terrain_info(myAgent.map.get_map_data(), ene_loc)
                if enemy['move_state'] != MoveState.Shelter and terrain not in [TerrainType.Forest,
                                                                                TerrainType.Resident]:
                    targets[2].append(ene_id)
                else:
                    targets[3].append(ene_id)
        else:  # 车辆算子的目标
            terrain = get_terrain_info(myAgent.map.get_map_data(), ene_loc)
            if enemy['move_state'] != MoveState.Shelter and terrain not in [TerrainType.Forest,
                                                                            TerrainType.Resident]:
                targets[2].append(ene_id)
            else:
                targets[3].append(ene_id)
    return targets


def get_jm_action(obj_id, myAgent):
    """从myAgent.observation['jm_points']中获取有关obj_id算子的间瞄行动，已经爆炸的不算"""
    for jm_info in myAgent.observation['jm_points']:
        if jm_info['obj_id'] and jm_info['status'] == 0:  # 正在飞行的才算
            if jm_info['obj_id'] == obj_id:
                return jm_info
    return None


def check_keep_infantry_in_hex(loc, enemies_info):
    """判断六角格loc中是否有压制状态的步兵，用于炮兵目标选择"""
    for enemy_info in enemies_info:
        if enemy_info['spread_flag']:
            enemy = enemy_info['operator']
            if enemy['cur_hex'] == loc:
                if enemy['keep'] == 1:
                    if enemy['type'] == BopType.Infantry:
                        return True
    return False


def get_min_dist_loc(candidate_locs, refer_locs, myAgent):
    """根据距离refer_locs远近从candidate_locs中筛选六角格"""
    map_size = myAgent.get_map_size()
    candidate_locs_ind = mapid_2_matind(np.array(candidate_locs), map_size)
    refer_locs_ind = mapid_2_matind(np.array(refer_locs), map_size)
    dist_matrix = myAgent.my_map['dist_matrix'][candidate_locs_ind.reshape(-1, 1), refer_locs_ind.reshape(1, -1)]
    avg_dist_vec = np.mean(dist_matrix, axis=1)
    best_ind = np.argmin(avg_dist_vec)
    return candidate_locs[best_ind]


def get_group_by_obj_id(obj_id, factionAI):
    for group_name, info in factionAI.group_info.items():
        if obj_id in info['obj_ids']:
            return group_name
    return None


def get_init_group_by_obj_id(obj_id, factionAI):
    for group_name, info in factionAI.group_static.items():
        if obj_id in info:
            return group_name
    return None
